#include "mod_network.h"
#include <stdio.h>
#include <stdlib.h>
/*
 * Codice Modulo = 13
 */
int create_TCP_server_socket(int port, int backlog, struct sockaddr_in *addr)
{
    int sockd;

    if((sockd = socket(AF_INET, SOCK_STREAM,0)) < 0)
    {
        return -1;
    }

    memset((void*)addr, 0, sizeof(*addr));

    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = htonl(INADDR_ANY);
    addr->sin_port = htons(port);

    if((bind(sockd, (struct sockaddr*)addr, sizeof(*addr))) < 0)
    {
        return -3;
    }

    if((listen(sockd, backlog)) <0 )
    {
        return -4;
    }
    return sockd;
}
int create_TCP_client_socket(char IP[], int port, struct sockaddr_in *addr)
{
    int sockd;

    if((sockd = socket(AF_INET, SOCK_STREAM,0)) < 0)
    {
        return -1;
    }

    memset((void*)addr, 0, sizeof(*addr));

    addr->sin_family = AF_INET;
    if((inet_pton(AF_INET, IP, &addr->sin_addr)) <= 0)
    {
        return -2;
    }
    addr->sin_port = htons(port);
    return sockd;
}
int connect_with_server(int *sd, struct sockaddr_in *addr)
{
    if((connect(*sd, (struct sockaddr*)addr, sizeof(*addr))) < 0)
    {
        return -1;
    }
    return 0;
}
int accept_connection(int *sd, struct sockaddr_in* addr)
{
    int sockd;
    socklen_t sl;
    sl = sizeof(*addr);
    if((sockd = accept(*sd, (struct sockaddr*)addr, &sl)) < 0)
    {
        return -1;
    }
    return sockd;
}
int readn(int sd, void* buffer, int dim)
{
    int nleft, nread;
    char* ptr;

    ptr = buffer;
    nleft = dim;
    while(nleft > 0)
    {
        if((nread = read(sd, ptr, nleft )) < 0)
        {
            if(errno == EINTR)
            {
                nread = 0;
            }
            else
            {
                return -1;
            }
        }
        else if(nread == 0)
        {
            break;
        }
        nleft -= nread;
        ptr += nread;
    }
    return nleft;
}
int writen(int sd, void* buffer, int dim)
{
    int nleft, nwritten;
    const char* ptr;
    ptr = buffer;
    nleft = dim;
    while(nleft > 0)
    {
        if((nwritten = write(sd, ptr, nleft)) <=0)
        {
            if((nwritten < 0) && (errno = EINTR))
            {
                nwritten = 0;
            }
            else
            {
                return -1;
            }
        }
        nleft -= nwritten;
        ptr += nwritten;
    }
    return nleft;
}
int close_socket(int *sd)
{
    if((close(*sd)) < 0)
    {
        return -1;
    }
    return 0;
}
int get_host_information(int *sd, struct sockaddr_in* addr, int *port, char IP[])
{
    socklen_t sl;
    sl = sizeof(*addr);
    if((getsockname(*sd, (struct sockaddr*)addr, &sl)) < 0)
    {
        return -1;
    }
    *port = ntohs(addr->sin_port);
    inet_ntop(AF_INET, addr, IP, INET6_ADDRSTRLEN);
    return 0;
}
int get_peer_information(int *sd, struct sockaddr_in* addr, int *port, char IP[])
{
    socklen_t sl;
    sl = sizeof(*addr);
    if((getpeername(*sd, (struct sockaddr*) addr, &sl))<0)
    {
        return -1;
    }
    *port = ntohs(addr->sin_port);
    sscanf(inet_ntoa(addr->sin_addr),"%s",IP);
    //inet_ntop(AF_INET, addr, IP, INET_ADDRSTRLEN);//INET6_ADDRSTRLEN
    return 1;
}

int myPing(char *ipaddr, int port){
/*
 * Restituisce una metrica rappresentata da un
 * numero intero. Utilizza per scegliere miglior
 * peer da cui scaricare. Prende in ingresso l'
 * indirizzo ip in formato stringa e la porta.
 */
    struct sockaddr_in sad;

    int sd;
    struct timeval start,stop;
    int ret=0;

    gettimeofday(&start,NULL);

    if((sd=socket(AF_INET,SOCK_STREAM,0))<0){
        return 999999;
    }

    memset((void *)&sad,0,sizeof(sad));
    sad.sin_family = AF_INET;
    sad.sin_port = htons(port);
    inet_pton(AF_INET,ipaddr,&sad.sin_addr);

    if(connect(sd,(struct sockaddr *)&sad,sizeof(sad))<0){
        return 999999;
    }

    close(sd);

    gettimeofday(&stop,NULL);

    ret = ((stop.tv_sec+stop.tv_usec)-(start.tv_sec+start.tv_usec))/1000;

    return ret;

}
